package jhunter.dao.ddl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import jhunter.dao.EntityContainer;
import jhunter.dao.EntityExt;

import org.hibernate.cfg.Environment;
import org.hibernate.cfg.Mappings;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PrimaryKey;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;

import schema.ext.AppExt;
import schema.vo.module.Property;
import schema.vo.module.Relationship;



/**
 * 创建表结构
 * 
 * @author jkyang
 * 
 */
public class SchemaGenerator {

	private EntityContainer entityContainer;
	
	private String hibernateDialect;
	private String url;
	private String driverClassName;
	private String username;
	private String password;

	protected Table getTable(EntityExt entity, Mappings mappings) {
		String schema = null;
		String catalog = null;
		String name = entity.getTableName();
		String subselect = null;
		boolean isAbstract = false;

		TableEx table = (TableEx) mappings.getTable(schema, catalog, name);
		if (table != null) {
			return table;
		}
		table = (TableEx) mappings.addTable(schema, catalog, name, subselect, isAbstract);
		PrimaryKey pk = new PrimaryKey();
		pk.setTable(table);
		Map<String, Column> propMap = new HashMap<String, Column>();
		for (String prop : entity.getProperties()) {
			Property p = entity.getPropsMap().get(prop);

			Column column = new Column();
			column.setName(entity.getColumnName(prop));
			SimpleValue simpleValue = new SimpleValue(mappings, table);
			simpleValue.setTypeName(entity.getHibernateTypeName(prop));
			column.setValue(simpleValue);
			column.setNullable(p.isNotNull());

			column.setLength(entity.getColumnLength(prop));
			if (entity.getHibernateTypeName(prop).toLowerCase().endsWith("decimal")) {
				column.setPrecision(column.getLength() <= 0 ? 19 : column.getLength());
			} else {
				column.setPrecision(column.getLength());
			}
			column.setComment(p.getLabel());
			column.setScale(p.getScale() == null ? 0 : Integer.valueOf(p.getScale()));

			table.addColumn(column);
			/**
			 * 设置主键
			 */
			if (entity.getPrimaryProp().equals(prop)) {
				pk.addColumn(column);
			}
			propMap.put(prop, column);
		}
		if (pk.getColumnSpan() > 0) {
			table.setPrimaryKey(pk);
		}

		/**
		 * 设置Unique
		 */
		Set<String> s = entity.getUniqueMap().keySet();
		if (s.size() > 0) {
			for (String p : s) {
				UniqueKey uk = new UniqueKey();
				uk.setTable(table);
				uk.setName(p);
				List<String> prs = entity.getUniqueMap().get(p);
				for (String string : prs) {
					uk.addColumn(propMap.get(string));
				}
				table.addUniqueKey(uk);
			}
		}
		/**
		 * 设置index-name TODO
		 */

		/**
		 * 设置FK String keyName, List keyColumns, String
		 * referencedEntityName,List referencedColumns
		 */
		List<String> lists = entity.getRelationships();
		if (lists != null) {
			for (String r : lists) {
				Relationship  relationship = entity.getRelationshipsMap().get(r);
				if (relationship.isPhysical() && !relationship.isToMany()) {
					String ukName = "uk_" + entity.getTableName() + "_" + relationship.getName(); // TODO
																									// 长度限制
					List<Column> keyColumns = new ArrayList<Column>();
					keyColumns.add(propMap.get(relationship.getSourceProperty()));
					EntityExt rentity = entity.getRefEntity(relationship.getTargetId());
					List<Column> foreignColumns = new ArrayList<Column>();
					Column foreignColumn = new Column();
					foreignColumn.setName(rentity.getColumnName(relationship.getTargetProperty()));
					foreignColumns.add(foreignColumn);

					table.createForeignKey(ukName, keyColumns, getTable(rentity, mappings), foreignColumns);

					// table.createForeignKey(ukName, keyColumns,
					// rentity.getTableName(), foreignColumns);
				}
			}
			
			List<String> props = entity.getProperties();
			for (String p : props) {
				Property pp = entity.getPropsMap().get(p);
				if (pp.getRefName() != null && pp.getRefName().trim().length() > 0) {
					String ukName = "uk_" + entity.getTableName() + "_" + pp.getRefName();
					List<Column> keyColumns = new ArrayList<Column>();
					keyColumns.add(propMap.get(p));
					EntityExt rentity = entity.getRefEntity(pp.getRefEntity());
					List<Column> foreignColumns = new ArrayList<Column>();
					Column foreignColumn = new Column();
					foreignColumn.setName(rentity.getColumnName(pp.getRefEntityProperty()));
					foreignColumns.add(foreignColumn);
					table.createForeignKey(ukName, keyColumns, getTable(rentity, mappings), foreignColumns);
				}
			}
		}
		return table;
	}

	/**
	 * 设定连接信息，表，comment, pk, uk, index, foreignKeys...
	 * 
	 * @param entity
	 */
	public void create(EntityExt entity) {
		List<EntityExt> entities  = new ArrayList<EntityExt>();
		entities.add(entity);
		create(entities);
	}
	
	public void create(List<EntityExt> entities) {
		ConfigurationEx configuration = createConfigurationEx();
		Mappings mappings = configuration.createMappings();
		for (EntityExt entityExt : entities) {
			this.getTable(entityExt, mappings);
		}
		SchemaUpdate update = new SchemaUpdate(configuration);
		update.execute(true, true);
	}
	
	protected ConfigurationEx createConfigurationEx() {
		ConfigurationEx configuration = new ConfigurationEx();
		configuration.setProperty(Environment.DIALECT, hibernateDialect);
		// configuration.setProperty(Environment.DEFAULT_SCHEMA, null);
		configuration.setProperty(Environment.URL, url);
		configuration.setProperty(Environment.DRIVER, driverClassName);
		configuration.setProperty(Environment.USER, username);
		configuration.setProperty(Environment.PASS, password);
		return configuration;
	}

	public void create(AppExt context) {
		
		ConfigurationEx configuration = createConfigurationEx();
		Mappings mappings = configuration.createMappings();
		Map<String, EntityExt>  maps = this.entityContainer.getContainer();
		if(maps != null) {
			for (EntityExt entity : maps.values()) {
				this.getTable(entity, mappings);
			}
		}
		SchemaUpdate update = new SchemaUpdate(configuration);
		update.execute(true, true);
	}

	public EntityContainer getEntityContainer() {
		return entityContainer;
	}

	public void setEntityContainer(EntityContainer entityContainer) {
		this.entityContainer = entityContainer;
	}

	public String getHibernateDialect() {
		return hibernateDialect;
	}

	public void setHibernateDialect(String hibernateDialect) {
		this.hibernateDialect = hibernateDialect;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	
	public String getDriverClassName() {
		return driverClassName;
	}

	public void setDriverClassName(String driverClassName) {
		this.driverClassName = driverClassName;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	

}
